package com.xuesinuo.ots.config.spring;

import java.io.Closeable;
import java.lang.reflect.Method;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import com.xuesinuo.ots.config.i18n.I18nTool;
import com.xuesinuo.ots.config.pojo.ImportException;
import com.xuesinuo.ots.tool.UserTool;
import com.xuesinuo.ots.util.IdUtil;
import com.xuesinuo.ots.util.StringUtil;
import com.xuesinuo.xtool.Np;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RestControllerAdvice
@RequiredArgsConstructor
@Slf4j
public class ApiResponseBodyHandler implements ResponseBodyAdvice<Object> {

    private final UserTool authTool4User;
    private final I18nTool i18nTool;

    /** 文件上传异常 */
    @ExceptionHandler(MultipartException.class)
    public ApiResult handleMultipartException(HttpServletRequest request, MultipartException ex) {
        ApiResult r = new ApiResult();
        r.setCode(ApiCode.param_error);
        r.setMessage(ex.getMessage());
        return r;
    }

    /** 文件上传异常 */
    @ExceptionHandler(ImportException.class)
    public ApiResult handleImportException(HttpServletRequest request, ImportException ex) {
        ApiResult r = new ApiResult();
        r.setCode(ApiCode.ok);
        r.setData(ex.getResult());
        return r;
    }

    /** 参数校验异常 */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult handleBindException(HttpServletRequest request, MethodArgumentNotValidException ex) {
        Map<String, String> data = ex.getBindingResult().getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
        ApiResult r = new ApiResult();
        r.setCode(ApiCode.param_error);
        String message = data.entrySet().stream().map(x -> x.getKey() + " " + x.getValue()).collect(Collectors.joining(","));
        r.setMessage(message);
        return r;
    }

    /** 参数校验异常 */
    @ExceptionHandler(NoResourceFoundException.class)
    public ApiResult handleNoResourceFoundException(HttpServletRequest request, NoResourceFoundException ex) {
        ApiResult r = new ApiResult();
        r.setCode(ApiCode.param_error);
        r.setMessage(ex.getMessage());
        return r;
    }

    /** HTTP报文格式异常 */
    @ExceptionHandler(HttpMediaTypeException.class)
    public ApiResult handleMediaTypeException(HttpServletRequest request, HttpMediaTypeException ex) {
        ApiResult r = new ApiResult();
        r.setCode(ApiCode.param_error);
        r.setMessage(ex.getMessage());
        return r;
    }

    /** HTTP报文格式异常 */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ApiResult handleHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException ex) {
        ApiResult r = new ApiResult();
        r.setCode(ApiCode.param_error);
        r.setMessage(ex.getMessage());
        return r;
    }

    /** 参数解析异常 */
    @ExceptionHandler(HttpMessageConversionException.class)
    public ApiResult hand(HttpServletRequest request, HttpMessageConversionException ex) {
        ApiResult r = new ApiResult();
        r.setCode(ApiCode.param_error);
        r.setMessage(ex.getMessage());
        return r;
    }

    /** 本项目自定义接口异常 */
    @ExceptionHandler(ApiException.class)
    public ApiResult handleException(HttpServletRequest request, ApiException ex) {
        ApiResult r = new ApiResult();
        r.setCode(ex.getCode());
        String i18nMessage = i18nTool.getTip(ex.getI18nTips(), ex.getI18nTipArgs());
        r.setMessage(StringUtil.isNotBlank(i18nMessage) ? i18nMessage : ex.getMessage());
        r.setData(ex.getData());
        if (Np.i(r.getCode()).in(ApiCode.sys_error)) {
            String uuid = IdUtil.getUUID();
            StringBuffer logBuffer = new StringBuffer();
            logBuffer.append("[" + uuid + "] ");
            if (request != null) {
                logBuffer.append(" method=" + request.getMethod());
                logBuffer.append(" url=" + request.getRequestURL());
                logBuffer.append(" params=" + request.getParameterMap());
            }
            log.error(authTool4User.getToken() + " " + authTool4User.getUserId() + " " + logBuffer.toString(), ex);
        }
        return r;
    }

    /** 通用异常 */
    @ExceptionHandler(Exception.class)
    public ApiResult handleException(HttpServletRequest request, Exception ex) {
        String uuid = IdUtil.getUUID();
        StringBuffer logBuffer = new StringBuffer();
        logBuffer.append("[" + uuid + "] ");
        if (request != null) {
            logBuffer.append(" method=" + request.getMethod());
            logBuffer.append(" url=" + request.getRequestURL());
            logBuffer.append(" params=" + request.getParameterMap());
        }
        log.error(authTool4User.getToken() + " " + authTool4User.getUserId() + " " + logBuffer.toString(), ex);
        ApiResult r = new ApiResult();
        r.setCode(ApiCode.sys_error);
        r.setMessage("System Error(" + uuid.substring(0, 6) + ")");
        return r;
    }

    /** 配置：是否使用自动打包 */
    @SuppressWarnings("null")
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        ApiResultPacking methodPackingAnn = returnType.getMethodAnnotation(ApiResultPacking.class);
        if (methodPackingAnn != null && methodPackingAnn.value()) {
            return true;
        } else {
            ApiResultPacking classPackingAnn = returnType.getContainingClass().getAnnotation(ApiResultPacking.class);
            if (classPackingAnn != null && classPackingAnn.value()) {
                return true;
            }
        }
        return false;
    }
    
    /** 配置：自动打包流程 */
    @SuppressWarnings("null")
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
            ServerHttpResponse response) {
        ApiResultPacking methodPackingAnn = returnType.getMethodAnnotation(ApiResultPacking.class);
        ApiResultPacking classPackingAnn = returnType.getContainingClass().getAnnotation(ApiResultPacking.class);
        ApiResultPacking usingPackingAnn = methodPackingAnn != null ? methodPackingAnn : classPackingAnn;
        if (usingPackingAnn.value()) {
            ApiResult apiResult = new ApiResult();
            apiResult.setCode(ApiCode.ok);
            apiResult.setData(body);

            Class<?> dataType = returnType.getMethod().getReturnType();
            String badResultTypeName = null;
            if (!Object.class.isAssignableFrom(dataType)) {// 非Object，即基本数据类型
                if (dataType == void.class) {
                    apiResult.setData(new HashMap<>());
                } else {
                    badResultTypeName = dataType.toString();
                }
            } else if (CharSequence.class.isAssignableFrom(dataType)) {// 字符串
                badResultTypeName = "string";
            } else if (Character.class.isAssignableFrom(dataType)) {// 字符
                badResultTypeName = "char";
            } else if (TemporalAccessor.class.isAssignableFrom(dataType)) {// JAVA8 时间存取器
                badResultTypeName = "date";
            } else if (Date.class.isAssignableFrom(dataType)) {// 日期
                badResultTypeName = "date";
            } else if (Number.class.isAssignableFrom(dataType)) {// 数字
                badResultTypeName = "number";
            } else if (Boolean.class.isAssignableFrom(dataType)) {// 布尔
                badResultTypeName = "boolean";
            } else if (Iterable.class.isAssignableFrom(dataType)) {// 集合
                badResultTypeName = "collection(iterable)";
            } else if (dataType.isArray()) {// 数组
                badResultTypeName = "array";
            } else if (Closeable.class.isAssignableFrom(dataType)) {// 可关闭的
                badResultTypeName = "closeable";
            }
            if (badResultTypeName != null) {
                Method method = returnType.getMethod();
                String methodName = returnType.getContainingClass().getName() + "#" + method.getName();
                throw new RuntimeException("Api不支持返回类型：" + badResultTypeName + "  " + methodName);
            }

            return apiResult;
        }
        return body;
    }

}
